namespace SimpleWebServices.Configuration
{
    using System;
    using System.Configuration;
    using System.Diagnostics.CodeAnalysis;
    using System.Reflection;
    using System.Xml;
    using System.Xml.Serialization;
    using SimpleWebServices.Web.UI.Html;

    [XmlRoot("html")]
    public sealed class HtmlElementConfiguration : IConfigurationSectionHandler
    {
        private static HtmlElementCollection _elements;

        public HtmlElementConfiguration()
        {
            this.Elements = new HtmlElementCollection();
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Can't deserialize if the setter is private.")]
        [XmlElement("elements")]
        public HtmlElementCollection Elements
        {
            get;
            set;
        }

        [XmlAttribute("whitespace")]
        public bool WhiteSpace
        {
            get;
            set;
        }

        private static HtmlElementCollection AssemblyElements
        {
            get
            {
                if (null == _elements)
                {
                    _elements = new HtmlElementCollection();

                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        foreach (Type type in assembly.GetTypes())
                        {
                            if (!type.IsAbstract && !type.IsGenericType && null != type.GetInterface("IElementSerializable"))
                            {
                                _elements.Add((IElementSerializable)Activator.CreateInstance(type));
                            }
                        }
                    }
                }

                return _elements;
            }
        }

        public static IElementSerializable Deserialize(string name, string type, string xml)
        {
            if (null == name)
            {
                throw new ArgumentNullException("name");
            }
            else if (0 == name.Length)
            {
                throw new ArgumentOutOfRangeException("name");
            }
            else if (null != type && 0 == type.Length)
            {
                throw new ArgumentOutOfRangeException("type");
            }
            else if (null == xml)
            {
                throw new ArgumentNullException("xml");
            }
            else if (0 == xml.Length)
            {
                throw new ArgumentOutOfRangeException("xml");
            }

            IElement result = null;

            foreach (IElementSerializable element in HtmlElementConfiguration.AssemblyElements)
            {
                result = element.Deserialize(name, type, xml);
                if (null != result)
                {
                    break;
                }
            }

            return result;
        }

        public static HtmlElementConfiguration Settings()
        {
            return HtmlElementConfiguration.Settings("simplewebservices/html");
        }

        public static HtmlElementConfiguration Settings(string sectionName)
        {
            HtmlElementConfiguration result = ConfigurationManager.GetSection(sectionName) as HtmlElementConfiguration;

            return result ?? new HtmlElementConfiguration();
        }

        object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section)
        {
            HtmlElementConfiguration result = new HtmlElementConfiguration();

            if (section != null)
            {
                try
                {
                    result = section.OuterXml.Deserialize<HtmlElementConfiguration>();
                }
                catch (InvalidOperationException)
                {
                    throw new ConfigurationErrorsException("Configured elements must implement IElement.", section);
                }
            }

            foreach (IElementSerializable element in HtmlElementConfiguration.AssemblyElements)
            {
                result.Elements.Add(element);
            }

            return result;
        }
    }
}